﻿using CrmField.Win8App;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using System;
using System.ComponentModel;
using System.Threading.Tasks;


namespace CRMField.Win8App.Common
{
    /// <summary>
    /// Singleton to Manage the CRM Context for the Application
    /// </summary>
    public static class CrmContextManager
    {
        /// <summary>
        /// Single CRM Context for the Application
        /// </summary>
        public static CrmContext Context { get; private set; }

        private static OrganizationServiceConfiguration _svcConfig;
        private static OrganizationServiceProxy _organizationServiceProxy;

        /// <summary>
        /// Static constructor for the Singleton
        /// </summary>
        static CrmContextManager()
        {
            

            string AppName = (string)App.Current.Resources["AppName"];

            var passwordVault = new Windows.Security.Credentials.PasswordVault();

            try
            {
                var credentials = passwordVault.FindAllByResource(AppName);

                //we've got a credential
                var credential = credentials[0];
                credential.RetrievePassword();
                Context = new CrmContext(credential.UserName, credential.Password);
            }
            catch (Exception)
            {
                //no credentials yet
                Context = new CrmContext();
            }            
        }

        /// <summary>
        /// Method that provides a connection to CRM
        /// It takes care of the initialisation if this has not been performed before.
        /// </summary>
        /// <returns>CRM Organisation Service configured</returns>
        public static async Task<OrganizationServiceProxy> GetConnectionAsync()
        {
            try
            {
                #region Initialise Service Configuration Metadata if needed
                if (_svcConfig == null)
                {
                    var url = "";
                    if (!CrmContextManager.Context.URL.EndsWith("/"))
                    {
                        url = string.Format(@"{0}/XRMServices/2011/Organization.svc", Context.URL);
                    }
                    else
                    {
                        url = string.Format(@"{0}XRMServices/2011/Organization.svc", Context.URL);
                    }
                    _svcConfig = await ExtensionMethods.CreateOrganizationConfigurationAsync(new Uri(url));
                }
                #endregion

                #region Check if we already have a service proxy with a valid token
                if ((_organizationServiceProxy != null) && (_organizationServiceProxy.SecurityTokenReponse != null))
                {
                    if (_organizationServiceProxy.SecurityTokenReponse.Expires > DateTime.Now)
                    {
                        return _organizationServiceProxy;
                    }
                }
                #endregion

                #region Perform authentication and obtain Service Configuration
                var credentials = new AuthenticationCredentials();
                var login = credentials.ClientCredentials.UserName;
                login.UserName = Context.UserName;
                login.Password = Context.Password;

                await _svcConfig.AuthenticateAsync(credentials);

                var isvcConfig = _svcConfig as IServiceConfiguration<IOrganizationService>;

                #endregion

                // Create the service
                _organizationServiceProxy = new OrganizationServiceProxy(
                                                    isvcConfig,
                                                    credentials.SecurityTokenResponse);

            }
            catch (Exception)
            {
                throw;
            }

            return _organizationServiceProxy;
        }

        /// <summary>
        /// Check whether we have username, password and url details available
        /// </summary>
        /// <returns>true if username, password and url are available</returns>
        public static bool IsValid()
        {
            return !string.IsNullOrEmpty(CrmContextManager.Context.URL) &&
                !string.IsNullOrEmpty(CrmContextManager.Context.UserName) &&
                !string.IsNullOrEmpty(CrmContextManager.Context.Password);
        }

    }

    /// <summary>
    /// Holds the CRM Context properties for connection
    /// It also handles persiting the information on PasswordVault and Roaming Settings
    /// </summary>
    public class CrmContext
    {
        public CrmContext()
        { }

        public CrmContext(string UserName, string Password)
        {
            this._user = UserName;
            this._password = Password;
        }

        public CrmContext(string UserName, string Password, string url)
            : this(UserName, Password)
        {
            this._url = url;
        }

        private string _user;
        public string UserName
        {
            get { return _user; }
            set
            {
                _user = value;
                SaveCredentials();
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("UserName"));
            }
        }

        private string _password;
        public string Password
        {
            get { return _password; }
            set
            {
                _password = value;
                SaveCredentials();
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Password"));
            }
        }

        private bool? _savePassword;
        public bool SavePassword
        {
            get
            {

                if (!_savePassword.HasValue)
                {
                    _savePassword = Windows.Storage.ApplicationData.Current.RoamingSettings.Values["SavePassword"] as bool?;
                    if (!_savePassword.HasValue) _savePassword = false;
                }
                return (bool)_savePassword;
            }
            set
            {
                _savePassword = value;
                Windows.Storage.ApplicationData.Current.RoamingSettings.Values["SavePassword"] = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("SavePassword"));
            }
        }

        private string _url;
        public string URL
        {
            get
            {
                if (string.IsNullOrEmpty(_url))
                {
                    _url = Windows.Storage.ApplicationData.Current.RoamingSettings.Values["OrgServiceUrl"] as string;
                }
                return _url;
            }
            set
            {
                _url = value;
                Windows.Storage.ApplicationData.Current.RoamingSettings.Values["OrgServiceUrl"] = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("URL"));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void SaveCredentials()
        {
            if (this.SavePassword)
            {
                string AppName = (string)App.Current.Resources["AppName"];
                var passwordVault = new Windows.Security.Credentials.PasswordVault();
                passwordVault.Add(new Windows.Security.Credentials.PasswordCredential(AppName, this.UserName, this.Password));
            }
        }
    }
}
